#include "GameApp.h"

/*******************************************************************
					PRIVATE Class Functions
*******************************************************************/


void GameApp::UpdateAI(LPDIRECT3DDEVICE9 pDevice, float elapsedTime) {
	for(list<WorldObject>::iterator wIter = m_lWorldObjects.begin(); wIter != m_lWorldObjects.end(); wIter++) {	
		// Pick target/destination point
		if(MakeAISmart(elapsedTime, &(*wIter))) {
			to_deleteW.push_back(wIter);
		} else {
			// Update for movement, rotation, and weapon cooldown
			wIter->Update(elapsedTime);
			// Update the AI's shooting, add projectiles when shooting
			AIShoot(pDevice,elapsedTime,&(*wIter));
			// set up dead reckoning
			wIter->meshInst.setPrevPos(*wIter->meshInst.getPosition());
			wIter->physicsInfo.m_prevDirLook = wIter->physicsInfo.dirLook;
		}
	}

	list<WorldObject*> deleteList;
	ITERATOR(WorldObject*) targIter;
	ITERATOR(WorldObject*) iterSelf;
	// Delete all units that have been killed
	for(t_dIterW = to_deleteW.begin(); t_dIterW != to_deleteW.end(); t_dIterW++) {
		// delete self from target's target list
		if((*t_dIterW)->m_targetUnit != NULL) {
			for(iterSelf = (*t_dIterW)->m_targetUnit->unitsTargeting.begin();
				iterSelf != (*t_dIterW)->m_targetUnit->unitsTargeting.end(); iterSelf++) {
					if((*iterSelf)->key == (*t_dIterW)->key) {
						deleteList.push_back(*iterSelf);
					}
			}
			for(iterSelf = deleteList.begin(); iterSelf != deleteList.end(); iterSelf++) {
				deleteList.erase(iterSelf);
				break;
			}
		}
		// set self's target list units to wandering, etc...
		for(targIter = (*t_dIterW)->unitsTargeting.begin(); targIter != (*t_dIterW)->unitsTargeting.end(); targIter++) {
			(*targIter)->m_state = wandering;
			(*targIter)->m_targetUnit = NULL; // *** very important ***
		}
		m_lWorldObjects.erase(*t_dIterW);
	}
	to_deleteW.clear(); // clear the list (not really needed, but as a precaution)
}

// begin:ai stuff
D3DXVECTOR3 GameApp::recalcRandPosOnMap() {
		// calc a new position to float to given a the map bounds
		int randX, randY, randZ;
		int xRange = (int)(floorf(m_mapBoundsBR.x - m_mapBoundsTL.x));
		int yRange = (int)(floorf(m_mapBoundsBR.y - m_mapBoundsTL.y));
		int zRange = (int)(floorf(m_mapBoundsTL.z - m_mapBoundsBR.z));

		if(xRange != 0)
			randX = (int)((rand()% xRange) + m_mapBoundsTL.x);
		else
			randX = (int)m_mapBoundsTL.x;
		
		if(yRange != 0)
			randY = (int)((rand()% yRange) + m_mapBoundsTL.y);
		else
			randY = (int)m_mapBoundsTL.y;
		if(zRange != 0)
			randZ = (int)((rand()% zRange) + m_mapBoundsBR.z);
		else
			randZ = (int)m_mapBoundsBR.z;

		return D3DXVECTOR3((float)randX,(float)randY,(float)randZ);
}
D3DXVECTOR3 GameApp::getDir(D3DXVECTOR3 initPos, D3DXVECTOR3 finalPos) {
	D3DXVECTOR3 diff = finalPos - initPos;
	D3DXVec3Normalize(&diff,&diff);

	return diff;
}
void GameApp::updateLook(WorldObject * wo, float elapsedTime) {
	// calc a new position within the given map bounds
	D3DXVECTOR3 curPos = *wo->meshInst.getPosition();
	D3DXVECTOR3 dirMax = D3DXVECTOR3(cos(ToRad(ROTATE_SPEED))*elapsedTime,0,sin(ToRad(ROTATE_SPEED))*elapsedTime);
	
	//ROTATE_SPEED
	// targetDir if no limitation                                                                                                                                                                                                           
	D3DXVECTOR3 targetLook = getDir(*wo->meshInst.getPosition( ),wo->physicsInfo.destination); 
	D3DXVECTOR3 curLook = wo->physicsInfo.dirLook;
		
	if(targetLook.x > curLook.x){
		wo->physicsInfo.dirLook.x += dirMax.x;
	} else {
		wo->physicsInfo.dirLook.x -= dirMax.x;
	}
	if(targetLook.z > curLook.z){
		wo->physicsInfo.dirLook.z += dirMax.z;
	} else {
		wo->physicsInfo.dirLook.z -= dirMax.z;
	}
	D3DXVec3Normalize(&wo->physicsInfo.dirLook,&wo->physicsInfo.dirLook);

}
// ignores the value of y
bool GameApp::isInTransitTowardsDest(WorldObject * wo) {
	D3DXVECTOR3 dir = getDir(*wo->meshInst.getPosition(),wo->physicsInfo.destination);
	dir.y = 0;
	D3DXVECTOR3 dirLook = wo->physicsInfo.dirLook;
	dirLook.y = 0;

	return (dir.x == dirLook.x && dir.z == dirLook.z);
}
void GameApp::setNewRandDest(WorldObject * wo,float elapsedTime) {
	D3DXVECTOR3 curPos = *wo->meshInst.getPosition();
	D3DXVECTOR3 newPos = recalcRandPosOnMap();	// get random location on map
	wo->physicsInfo.destination = newPos;
	updateLook(wo,elapsedTime);
}

void calcAITargets();
void updateAIMovement();

// SHOULD REFACTOR INTO CLASS AND MAKE INTO STATE MACHINE
bool GameApp::MakeAISmart(float elapsedTime, WorldObject * it) {

	// for thru world objects
	//	for(list<WorldObject>::iterator	it = m_lWorldObjects.begin(); it != m_lWorldObjects.end(); it++) {
	if(it->health <= 0.0f) {
		return true;
	} else {
		// pick random spots on the map to look for player. if player is within range, go to the player. else if the enemy is within a threshhold of the spot, move to a new one 
		if(it->m_state == uninitialized){ // check if not initialized, if so set a random point in the map (update to detect for player)
			it->m_state = wandering;
			// calc a new position to float to given a the map bounds
			it->physicsInfo.destination = recalcRandPosOnMap();
			updateLook(&(*it), elapsedTime);
		}
		// check if the current ai is still in transit towards destination if wandering
		WorldObject * temp = &(*it);
		if(it->m_state == wandering ){
			updateLook(temp, elapsedTime);
			// check if the unit is close enough to destination, if so, recalc destination
			D3DXVECTOR3 curPos = *(it->meshInst.getPosition());
			if(calDist(&it->physicsInfo.destination, &curPos) <= DIST_TO_DEST_THRESHOLD){
				setNewRandDest(temp,elapsedTime);
			}
			// ENEMY VS. PLAYER
			if(it->alignment.compare("Enemy") == 0) {
				// check if unit is close to player, if so, follow player and switch states
				if(calDist(m_player.meshInst.getPosition(), &curPos) <= it->unitInfo.LOS){
					it->m_state = alerted;
					it->m_targetUnit = &m_player;
					// WARNING CHANGE TO UPDATE WHERE PLAYER IS HEADING
					it->physicsInfo.destination = *m_player.meshInst.getPosition();
					updateLook(&(*it), elapsedTime);
				} else {
					it->m_state = wandering;
				}
				// ALLY VS ENEMY
			} else if(it->alignment.compare("Ally") == 0) {
				for(ITERATOR(WorldObject) it2 = m_lWorldObjects.begin(); it2 != m_lWorldObjects.end(); it2++) {
					// check if unit is close to enemy, if so, follow enemy and switch states
					if(calDist(it->meshInst.getPosition(),it2->meshInst.getPosition()) <= it->unitInfo.LOS) {	
						if(it2->alignment.compare("Enemy") == 0 && it2->health > 0.0f) {
							it->m_state = alerted;
							it->m_targetUnit = &(*it2);
							it->m_targetUnit->unitsTargeting.push_back(&(*it));
							// WARNING CHANGE TO UPDATE WHERE ENEMEY IS HEADING
							it->physicsInfo.destination = *it2->meshInst.getPosition();
							updateLook(&(*it),elapsedTime);
						} else {
							it->m_state = wandering;
						}
					}
				}
			}
		} else if(it->m_state == alerted) {
			/** DO RANGE CHECK HERE: if target is out of sight then switch to wandering **/
			it->physicsInfo.destination = *it->m_targetUnit->meshInst.getPosition();
			updateLook(&(*it),elapsedTime);
			// check if destination is within LOS range, if it isnt, go back to wandering
			if(calDist(it->meshInst.getPosition(), it->m_targetUnit->meshInst.getPosition()) > it->unitInfo.LOS){
				it->m_state = wandering;
				// go thru m_target unit's list and remove itself from unitsTargeting
				for(list<WorldObject*>::iterator utIt = it->m_targetUnit->unitsTargeting.begin();
					utIt != it->m_targetUnit->unitsTargeting.end(); 
					utIt++){
						if(it->key == (*utIt)->key){
							it->m_targetUnit->unitsTargeting.erase(utIt);
							break;
						}

				}
			}
		}
	}
	return false;
}
// end:ai stuff

void GameApp::AIShoot(LPDIRECT3DDEVICE9 pDevice, float elapsedTime, WorldObject * wIter) {
	if(wIter->m_state == alerted) {
		if(calDist(wIter->m_targetUnit->meshInst.getPosition(),wIter->meshInst.getPosition()) <= wIter->unitInfo.shootRange) {

			for(wIter->weaIter = wIter->weapons.begin(); wIter->weaIter != wIter->weapons.end(); wIter->weaIter++) {
				// If the projectile is out of cooldown, set it's firetime to 0, else add the elapsed time to the firetime.
				if(wIter->weaIter->fireTime >= wIter->weaIter->fireRate)
					wIter->weaIter->fireTime = 0.0f;
				// and if the weapon requires the left mouse button for activation...
				if(wIter->weaIter->type == "Bullet") {
					// and if the weapon is not on cooldown, then fire the weapon (no ammo. limit) 
					if(wIter->weaIter->fireTime != 0.0f) {
					} else {
						// CREATE PROJECTILE
						m_lProjObjects.push_front(*createBullet(pDevice,&(*wIter)));
						// AI MUZZLE FLASH
						makeAiMuzzleFlash(pDevice,&(*wIter));
					}
				}
			}

		}
	}
}


// Begin rendering
void GameApp::BeginScene(LPDIRECT3DDEVICE9 pDevice) {
	pDevice->SetTransform(D3DTS_VIEW, m_camera.getViewMatrix());
	pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(100, 100, 100), 1.0f, 0);
	pDevice->BeginScene();
}

// End rendering
void GameApp::EndScene(LPDIRECT3DDEVICE9 pDevice) {
	pDevice->EndScene();
	pDevice->Present(0, 0, 0, 0);
}

// Render the ground/terrain
void GameApp::RenderGround(LPDIRECT3DDEVICE9 pDevice) {
	m_ground.Render(pDevice);
}

// Render the world objects (units and env. objects)
void GameApp::RenderEnvObjects(LPDIRECT3DDEVICE9 pDevice) {
	// Go through the EnvObjects list and render all within sight
	for(eIter = m_lEnvObjects.begin(); eIter != m_lEnvObjects.end(); eIter++) {
		if(m_camera.sphereInFrustrum(eIter->meshInst.getPosition(),eIter->meshInst.getBoundingRadius())) {
			eIter->Render(pDevice);
			m_objsRenderedCount++;
		}
	}
}

// Render the world objects (units and env. objects)
void GameApp::RenderWorldObjects(LPDIRECT3DDEVICE9 pDevice) {
	// Go through the WorldObjects list and render all within sight
	for(wIter = m_lWorldObjects.begin(); wIter != m_lWorldObjects.end(); wIter++) {
		if(m_camera.sphereInFrustrum(wIter->meshInst.getPosition(),wIter->meshInst.getBoundingRadius())) {
			wIter->Render(pDevice);
			m_objsRenderedCount++;
		}
	}
}

// Render the player
void GameApp::RenderPlayer(LPDIRECT3DDEVICE9 pDevice) {
	m_player.Render(pDevice);
	m_objsRenderedCount++;
}

// Render th player's direction point
void GameApp::RenderPlayerDirPoint(LPDIRECT3DDEVICE9 pDevice) {
	m_playerDest.Render(pDevice);
	m_objsRenderedCount++;
}

// Render the projectiles
void GameApp::RenderProjectiles(LPDIRECT3DDEVICE9 pDevice) {
	// Go through the ProjObjects list and render all within sight
	for(pIter = m_lProjObjects.begin(); pIter != m_lProjObjects.end(); pIter++) {
		if(m_camera.sphereInFrustrum(pIter->meshInst.getPosition(),pIter->meshInst.getBoundingRadius())) {
			pIter->Render(pDevice);
			m_objsRenderedCount++;
		}
	}
}

// Render the misc. objects (points, waypoints, becons, areas, etc.)
void GameApp::RenderMiscObjects(LPDIRECT3DDEVICE9 pDevice) {
	// Go through the MiscObjects list and render all within sight
	for(mIter = m_lMiscObjects.begin(); mIter != m_lMiscObjects.end(); mIter++) {
		if(m_camera.sphereInFrustrum(mIter->meshInst.getPosition(),mIter->meshInst.getBoundingRadius())) {
			mIter->Render(pDevice);
			m_objsRenderedCount++;
		}
	}
}

void GameApp::RenderMarkerObjects(LPDIRECT3DDEVICE9 pDevice) {
	for(markIter = m_lMarkerObjects.begin(); markIter != m_lMarkerObjects.end(); markIter++) {
		if(m_camera.sphereInFrustrum(markIter->meshInst.getPosition(),markIter->meshInst.getBoundingRadius())) {
			markIter->Render(pDevice);
			m_objsRenderedCount++;
		}
	}
}

// Render text
void GameApp::RenderText() {
	m_pTextSprite->Begin(D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE);


	// Display the FPS if it is activated
	if(g_ShowFPS)
		m_font.print(m_fps, 5, 5, D3DCOLOR_XRGB(255, 0, 0), m_pTextSprite);

	if(g_ShowNumObjectsRendered)
		m_font.print(m_objsRendered, 5, 30, D3DCOLOR_XRGB(255,0,0), m_pTextSprite);
	
	displayPlayerHP(); // display the player's health information

	if(m_outOfBoundsTimer != g_OutOfBoundsTimeMax) {
		m_mediumFont.print(m_outOfBoundsText, (m_pFramework->getWidth()/2)-50, (m_pFramework->getHeight()/2)-50, 
			D3DCOLOR_XRGB(255,0,0), m_pTextSprite);
	}


	m_pTextSprite->End();
}

// Render player's health information
void GameApp::displayPlayerHP() {
	char hp[15];
	sprintf_s(hp, sizeof(hp), "%.00f/%.00f HP", m_player.health, m_player.maxHealth);
	m_largeFont.print(hp, 1400, 950, D3DCOLOR_XRGB(0,100,0), m_pTextSprite);
}

// Update the HUD (Heads-Up Display)
void GameApp::UpdateHUD() {
	sprintf_s(m_fps, sizeof(m_fps), "%.1f fps", m_pFramework->getFPS()); // set the FPS information into m_fps
	sprintf_s(m_objsRendered, sizeof(m_objsRendered), "%i objects rendered", m_objsRenderedCount); // set the number of objects rendered information
}

// Update the camera
void GameApp::UpdateCamera() {
	// Set the camera's look position to the player's position, then update the camera
	// so that the camera is always at a specific offset from the player's position.
	m_camera.setLook(m_player.meshInst.getPosition());
	D3DXVECTOR3 inverse = -(m_player.physicsInfo.dirLook);
	static bool a = false;
	if(!m_TDView) {
		if(!a) {
			PRINT("dirLook [x] %.02f [y] %.02f [z] %.02f", m_player.physicsInfo.dirLook.x, 
				m_player.physicsInfo.dirLook.y, m_player.physicsInfo.dirLook.z);
			NEXT_LINE;
			PRINT("Inv. dirLook [x] %.02f [y] %.02f [z] %.02f", inverse.x, inverse.y, inverse.z);
			NEXT_LINE;
			
			D3DXVECTOR3 camPoint;
			camPoint.x = inverse.x * g_cameraOffset3P.x;
			camPoint.y = inverse.y * g_cameraOffset3P.y;
			camPoint.z = inverse.z * g_cameraOffset3P.z;
			PRINT("camPoint [x] %.02f [y] %.02f [z] %.02f", camPoint.x, camPoint.y, camPoint.z);
			NEXT_LINE;
			camPoint += *m_player.meshInst.getPosition();
			PRINT("playerPos [x] %.02f [y] %.02f [z] %.02f", m_player.meshInst.getPosition()->x, 
				m_player.meshInst.getPosition()->y, m_player.meshInst.getPosition()->z);
			NEXT_LINE;
			PRINT("camPoint offsetted [x] %.02f [y] %.02f [z] %.02f", camPoint.x, camPoint.y, camPoint.z);
			NEXT_LINE;
			
			
			a = true;
		}
	}
	m_camera.Update(&inverse, m_TDView);
}

// Update the player
void GameApp::UpdatePlayer(LPDIRECT3DDEVICE9 pDevice, float elapsedTime) {
	// Update player (pass mouse input information in). Rotation and movement is calculated within the update function.
	m_player.Update(m_TDView,(float)m_deltaX,(float)m_deltaY,(float)elapsedTime);
	// Handle the player's shooting
	playerShoot(pDevice,elapsedTime);
	playerOutOfBounds(elapsedTime);

	// Set player's dest/dir point (for debugging purposes)
	m_playerDest.meshInst.translateAbs(m_player.physicsInfo.dirPoint.x,m_player.physicsInfo.dirPoint.y,m_player.physicsInfo.dirPoint.z);
}

// In the event that the player shoots (left or right mouse buttons)...
void GameApp::playerShoot(LPDIRECT3DDEVICE9 pDevice, float elapsedTime) {
	if(m_leftMouseButton) { // if the player hit the left mouse button
		// Go though all of the player's weapons...
		for(m_player.weaIter = m_player.weapons.begin(); m_player.weaIter != m_player.weapons.end(); m_player.weaIter++) {
			// If the projectile is out of cooldown, set it's firetime to 0, else add the elapsed time to the firetime.
			if(m_player.weaIter->fireTime >= m_player.weaIter->fireRate)
				m_player.weaIter->fireTime = 0.0f;
			// and if the weapon requires the left mouse button for activation...
			if(m_player.weaIter->trigger == "LeftMouse") {
				if(m_player.weaIter->type == "Bullet") {
					// and if the weapon is not on cooldown, then fire the weapon (no ammo. limit) 
					if(m_player.weaIter->fireTime != 0.0f) {
					} else {		
						// Push the projectile into the ProjObject list
						m_lProjObjects.push_front(*createBullet(pDevice,&m_player));
						PRINT("Alignment ");	PRINT(m_player.alignment.c_str());		NEXT_LINE;

						// PLAYER MUZZLE FLASH
						makePlayerMuzzleFlash(pDevice);

						Sound snd;
						snd.initDirectSound(m_pFramework->getHandle());
						snd.setFilename("Sound\\MachineGun1.wav");
						snd.LoadWaveToSoundBuffer();
						if(slowmotion) {
							snd.setFreq(snd.getFreq(), 1.0f / g_SlowMotionModifier);
						}
						snd.playSound();
					}
				}
			}
		}
		m_leftMouseButton = false; // set the left mouse button to false, since it's button-down had been handled
								   // *note: if the mouse button is still down, this will not affect its function to fire continously
	}

	if(m_rightMouseButton) { // if the player hit the left mouse button
		// Go though all of the player's weapons...
		for(m_player.weaIter = m_player.weapons.begin(); m_player.weaIter != m_player.weapons.end(); m_player.weaIter++) {
			// If the projectile is out of cooldown, set it's firetime to 0, else add the elapsed time to the firetime.
			if(m_player.weaIter->fireTime >= m_player.weaIter->fireRate)
				m_player.weaIter->fireTime = 0.0f;
			// and if the weapon requires the left mouse button for activation...
			if(m_player.weaIter->trigger == "RightMouse") {
				if(m_player.weaIter->type == "Bomb") {
					// and if the weapon is not on cooldown, then fire the weapon (no ammo. limit) 
					if(m_player.weaIter->fireTime != 0.0f) {
					} else {
						// Push the projectile into the ProjObject list
						m_lProjObjects.push_front(*createBomb(pDevice,&m_player));
					}
				}
			}
		}
		m_rightMouseButton = false; // set the left mouse button to false, since it's button-down had been handled
								   // *note: if the mouse button is still down, this will not affect its function to fire continously
	}
}

ProjObject* GameApp::createBullet(LPDIRECT3DDEVICE9 pDevice, WorldObject* srcUnit) {
	// Create a new ProjObject
	ProjObject* pObj = new ProjObject(srcUnit->weaIter->modelFile);
	if(!pObj->Initialize(pDevice)) { // make sure it is initialized
		SHOWERROR("t_wObject.Initialize() - Failed", __FILE__, __LINE__);
	}
	// Set the information for the recently created ProjObject
	pObj->sourceUnit = srcUnit; // the source unit that fired this particular projectile
	pObj->alignment = srcUnit->alignment; // alignment of this projectile
	pObj->type = srcUnit->weaIter->type;
	pObj->physicsInfo.dirLook = srcUnit->physicsInfo.dirLook; // the direction the projectile is looking/traveling in
	pObj->physicsInfo.maxVelocity = srcUnit->weaIter->maxVelocity; // the maximum velocity of this projectile
	pObj->range = srcUnit->weaIter->range; // the range of this projectile
	pObj->damage = srcUnit->weaIter->weaponDmg;
	pObj->initialPosition = *srcUnit->meshInst.getPosition(); // the position that this projectile was fired in, useful
															  // since the range will be relative to it's fired position
	// Rotate and translate the projectile (rotation based on rotation of the source, translation offset so that the 
	// projectile is firing from the front of the unit, not from the middle)
	pObj->meshInst.rotateAbs(srcUnit->meshInst.getXRotation(),srcUnit->meshInst.getYRotation(),srcUnit->meshInst.getZRotation());
	D3DXVECTOR3 offset(*srcUnit->meshInst.getPosition());
	offset += srcUnit->physicsInfo.dirLook * g_BulletOffsetMultiplier;
	pObj->meshInst.translateAbs(offset.x,offset.y,offset.z);

	return pObj;
}

ProjObject* GameApp::createBomb(LPDIRECT3DDEVICE9 pDevice, WorldObject* srcUnit) {
	// Create a new ProjObject
	ProjObject* pObj = new ProjObject(srcUnit->weaIter->modelFile);
	if(!pObj->Initialize(pDevice)) { // make sure it is initialized
		SHOWERROR("t_wObject.Initialize() - Failed", __FILE__, __LINE__);
	}
	// Set the information for the recently created ProjObject
	pObj->sourceUnit = &m_player; // the source unit that fired this particular projectile
	pObj->alignment = srcUnit->alignment; // alignment of this projectile
	pObj->type = srcUnit->weaIter->type;
	pObj->physicsInfo.dirLook = srcUnit->physicsInfo.dirLook; // the direction the projectile is looking/traveling in
	pObj->physicsInfo.maxVelocity = srcUnit->physicsInfo.maxVelocity; // the maximum velocity of this projectile
	pObj->range = srcUnit->weaIter->range; // the range of this projectile
	pObj->damage = srcUnit->weaIter->weaponDmg;
	pObj->initialPosition = *srcUnit->meshInst.getPosition(); // the position that this projectile was fired in, useful
															  // since the range will be relative to it's fired position
	// Rotate and translate the projectile (rotation based on rotation of the source, translation offset so that the 
	// projectile is firing from the front of the unit, not from the middle)
	pObj->meshInst.rotateAbs(srcUnit->meshInst.getXRotation(),srcUnit->meshInst.getYRotation(),srcUnit->meshInst.getZRotation());
	D3DXVECTOR3 offset(*srcUnit->meshInst.getPosition());
	pObj->meshInst.translateAbs(offset.x,offset.y,offset.z);

	return pObj;
}

void GameApp::makePlayerMuzzleFlash(LPDIRECT3DDEVICE9 pDevice) {
	D3DXVECTOR3 pos;
	m_lights[2].Initialize(D3DLIGHT_POINT, g_MuzzleFlashRange);
	m_lights[2].setAtt(0.0f,0.2f,0.0f);
	pos = (*m_player.meshInst.getPosition()) + (m_player.physicsInfo.dirLook * g_MuzzleFlashDist);
	m_lights[2].setPosAbs(pos.x,pos.y + 1.0f,pos.z);
	m_lights[2].setAmbAbs(0.0f,0.0f,0.0f);
	m_lights[2].setDifAbs(1.5f,1.5f,1.5f);
	m_lights[2].setSpeAbs(0.3f,0.3f,0.3f);
	m_lights[2].setLight(pDevice,2);
	m_lights[2].enableLight(pDevice,true);
}

void GameApp::makeAiMuzzleFlash(LPDIRECT3DDEVICE9 pDevice, WorldObject* srcUnit) {
	if(g_AiMuzzleFlash) {
		for(int i = g_FreeLightStart; i<g_MAX_LIGHTS; i++) {
			if(!m_lights[i].getStatus()) {
				m_lights[i].Initialize(D3DLIGHT_POINT, g_MuzzleFlashRange);
				m_lights[i].setAtt(0.0f,0.2f,0.0f);
				D3DXVECTOR3 pos;
				pos = (*srcUnit->meshInst.getPosition()) + (srcUnit->physicsInfo.dirLook * g_MuzzleFlashDist);
				m_lights[i].setPosAbs(pos.x,pos.y + 1.0f,pos.z);
				m_lights[i].setAmbAbs(0.0f,0.0f,0.0f);
				m_lights[i].setDifAbs(1.5f,1.5f,1.5f);
				m_lights[i].setSpeAbs(0.3f,0.3f,0.3f);
				m_lights[i].setLight(pDevice,i);
				m_lights[i].enableLight(pDevice,true);
			}
		}
	}
}

void GameApp::makePlayerBombFlash(LPDIRECT3DDEVICE9 pDevice, ProjObject* pObj) {
	D3DXVECTOR3 pos;
	m_lights[2].Initialize(D3DLIGHT_POINT, g_BombFlashMultiplier*pObj->range);
	m_lights[2].setAtt(0.0f,0.2f,0.0f);
	pos = *pObj->meshInst.getPosition();
	m_lights[2].setPosAbs(pos.x,pos.y,pos.z);
	m_lights[2].setAmbAbs(0.3f,0.0f,0.0f);
	m_lights[2].setDifAbs(1.5f,0.2f,0.2f);
	m_lights[2].setSpeAbs(0.5f,0.1f,0.1f);
	m_lights[2].setLight(pDevice,3);
	m_lights[2].enableLight(pDevice,true);
}

void GameApp::makeAiBombFlash(LPDIRECT3DDEVICE9 pDevice, ProjObject* pObj) {
	if(g_AiMuzzleFlash) {
		for(int i = g_FreeLightStart; i<g_MAX_LIGHTS; i++) {
			if(!m_lights[i].getStatus()) {
				m_lights[i].Initialize(D3DLIGHT_POINT, g_BombFlashMultiplier*pObj->range);
				m_lights[i].setAtt(0.0f,0.2f,0.0f);
				D3DXVECTOR3 pos;
				pos = *pObj->meshInst.getPosition();
				m_lights[i].setPosAbs(pos.x,pos.y,pos.z);
				m_lights[i].setAmbAbs(0.0f,0.0f,0.0f);
				m_lights[i].setDifAbs(1.5f,1.5f,1.5f);
				m_lights[i].setSpeAbs(0.3f,0.3f,0.3f);
				m_lights[i].setLight(pDevice,i);
				m_lights[i].enableLight(pDevice,true);
			}
		}
	}
}

void GameApp::playerOutOfBounds(float elapsedTime) {
	if(m_outOfBoundsTimer <= 0.0f) {
		m_player.health = -1.0f;
	}
	if(isOutOfBounds(m_player.meshInst.getPosition())) {
		if(m_player.health > 0.0f) {
			m_outOfBoundsTimer -= elapsedTime;
			sprintf_s(m_outOfBoundsText, sizeof(m_outOfBoundsText), "OUT OF BOUNDS!\n%is", (int)m_outOfBoundsTimer);
		}
	} else {
		m_outOfBoundsTimer = g_OutOfBoundsTimeMax;
	}
}

bool GameApp::isOutOfBounds(D3DXVECTOR3* position) {
	if(position->x < m_mapBoundsTL.x || position->x > m_mapBoundsBR.x || 
		position->z > m_mapBoundsTL.z || position->z < m_mapBoundsBR.z) {
			return true;
	}
	return false;
}

// Update the projectiles (movement, collision, etc.)
void GameApp::UpdateProjectiles(LPDIRECT3DDEVICE9 pDevice, float elapsedTime) {
	DeleteProjectilesOutOfRange();
	ProjectileCollision(pDevice);
	UpdateProjectileMovement(elapsedTime);
}

// Delete the projectiles that went out of their range
void GameApp::DeleteProjectilesOutOfRange() {
	// Go through the ProjObject list and, if any projectiles went out of range, push them into the delete list
	// Deleting them on-the-go will result in an error
	for(pIter = m_lProjObjects.begin(); pIter != m_lProjObjects.end(); pIter++) {
		if(pIter->type == "Bullet") {
			if(calDist(pIter->meshInst.getPosition(),&pIter->initialPosition) > pIter->range) {
				to_deleteP.push_back(pIter);
			}
		}
	}
	for(t_dIterP = to_deleteP.begin() ;t_dIterP != to_deleteP.end(); t_dIterP++) {
		(*t_dIterP)->Release();
		m_lProjObjects.erase(*t_dIterP);
	}
	to_deleteP.clear(); // clear the list (not really needed, but as a precaution)
}

// Handle projectile collisions
void GameApp::ProjectileCollision(LPDIRECT3DDEVICE9 pDevice) {
	// remove the projectiles after impact (for impact "animation")
	for(pIter = m_lProjObjects.begin(); pIter != m_lProjObjects.end(); pIter++) {
		if(pIter->deleteNextUpdate)
			to_deleteP.push_back(pIter);
	}
	// Delete all projectiles that have been in a collision
	for(t_dIterP = to_deleteP.begin() ;t_dIterP != to_deleteP.end(); t_dIterP++) {
		(*t_dIterP)->Release();
		m_lProjObjects.erase(*t_dIterP);
	}
	to_deleteP.clear(); // clear the list (not really needed, but as a precaution)

	// Go through the ProjObject and WorldObject lists and check for any collision (projectile position to world object radius)
	for(pIter = m_lProjObjects.begin(); pIter != m_lProjObjects.end(); pIter++) {
		if(!pIter->deleteNextUpdate) {
			if(pIter->type == "Bullet") {
				for(wIter = m_lWorldObjects.begin(); wIter != m_lWorldObjects.end(); wIter++) {
					// For projectiles targeting aerial units...
					// If the projectile is of a certain alignment, and the current WorldObject iterator is of certain alignment, then continue...
					if((pIter->alignment == "Player" && wIter->alignment == "Enemy") || (pIter->alignment == "Ally" && wIter->alignment == "Enemy")
						|| (pIter->alignment == "Enemy" && wIter->alignment == "Ally")) {
						// Check for collision...
						if(calDist(pIter->meshInst.getPosition(),wIter->meshInst.getPosition()) < wIter->meshInst.getBoundingRadius()) {
							// Decrease the WorldObject's health by the projectile's damage
							wIter->health -= pIter->damage;
							pIter->deleteNextUpdate = true;
							pIter->meshInst.scaleAbs(5.0f,5.0f,1.5f);
						}
					}
				}
				// Check for player collision...
				if(pIter->alignment == "Enemy") {
					if(calDist(pIter->meshInst.getPosition(),m_player.meshInst.getPosition()) < m_player.meshInst.getBoundingRadius()) {
						// Decrease player's health by the projectile's damage
						m_player.health -= pIter->damage;
						pIter->deleteNextUpdate = true;
						pIter->meshInst.scaleAbs(5.0f,5.0f,1.5f);
					}
				}
			} else if(pIter->type == "Bomb") {
				if(pIter->meshInst.getYPosition() < g_BombDetonationHeight) {
					for(eIter = m_lEnvObjects.begin(); eIter != m_lEnvObjects.end(); eIter++) {
						if(eIter->destructable) {
							if(((pIter->alignment == "Player" || pIter->alignment == "Ally") && 
								(eIter->alignment == "Enemy" || eIter->alignment == "Neutral"))
								|| (pIter->alignment == "Enemy" && eIter->alignment != "Enemey")) {
									if(calDist(pIter->meshInst.getPosition(),eIter->meshInst.getPosition()) <= 
										(pIter->range + eIter->meshInst.getBoundingRadius())) {
										eIter->health -= pIter->damage;
									}
							}
						}
					}
					pIter->deleteNextUpdate = true;
					pIter->meshInst.scaleAbs(30.0f,30.0f,10.0f);
					if(pIter->alignment == "Player") {
						makePlayerBombFlash(pDevice,&(*pIter));
					} else {
						makeAiBombFlash(pDevice,&(*pIter));
					}
				}
			}
		}
	}
}

// Update the projectiles' movement
void GameApp::UpdateProjectileMovement(float elapsedTime) {
	// Go through the ProjObject list and update all current projectiles' movement
	for(pIter = m_lProjObjects.begin(); pIter != m_lProjObjects.end(); pIter++)
		pIter->Update(elapsedTime);
}

void GameApp::UpdateNightVision(LPDIRECT3DDEVICE9 pDevice) {
	if(m_n) {
		if(m_lights[1].getStatus())
			m_lights[1].enableLight(pDevice,false);
		else
			m_lights[1].enableLight(pDevice,true);
		m_n = false;
	}
}

void GameApp::UpdateLights(LPDIRECT3DDEVICE9 pDevice) {
	for(int i=g_PlayerLightsStart; i<g_MAX_LIGHTS; i++)
		m_lights[i].enableLight(pDevice, false);
}

void GameApp::UpdateEnvObjects(LPDIRECT3DDEVICE9 pDevice) {
	for(eIter = m_lEnvObjects.begin(); eIter != m_lEnvObjects.end(); eIter++) {
		eIter->Update(0.0f);
	}
}

void GameApp::UpdateMarkers(float elapsedTime) {
	for(markIter = m_lMarkerObjects.begin(); markIter != m_lMarkerObjects.end(); markIter++) {
		markIter->Update(elapsedTime);
		if(markIter->envObj->health <= 0.0f)
			to_deleteMark.push_back(markIter);
	}
	for(t_dIterMark = to_deleteMark.begin() ;t_dIterMark != to_deleteMark.end(); t_dIterMark++) {
		m_lMarkerObjects.erase(*t_dIterMark);
	}
	to_deleteMark.clear();
}

void GameApp::RenderLoading(LPDIRECT3DDEVICE9 pDevice) {
	BeginScene(pDevice);
	m_pTextSprite->Begin(D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE);

	m_mediumFont.print(m_loading, 600, 500, D3DCOLOR_XRGB(255,0,0), m_pTextSprite);
	m_smallFont.print(m_loadingDesc, 600, 560, D3DCOLOR_XRGB(255,255,0), m_pTextSprite);

	m_pTextSprite->End();
	EndScene(pDevice);
}
